#ifndef PHONGSHADER
#define PHONGSHADER

#include "Vector3.h"
#include <iostream>
#include <math.h>

#include "RenderingSystem.h"

using namespace std;

class PhongShader : public SurfaceShader {
    public:

        PhongShader() {

        }

        bool Validate() {
            return true;
        }

        void InternalShade(Material &_Material,
            Ray &_EyeRay,
            Vector3 &_HitLocation,
            SurfaceObject &_Object,
            vector<LightSource*> &_ListOfLightSources,
            RenderingSystem &_RenderingSystem)
        {

            float specular_power = 100.0;

            Vector3 LightDirection = Vector3::normalize(_ListOfLightSources.front()->GetPosition() - _HitLocation);
            float diffuse = max(Vector3::dot(_Object.GetNormal(_HitLocation), LightDirection), 0.0f);
            float specular = pow(max(Vector3::dot(LightDirection, 1.0*Vector3::reflect(_EyeRay.direction, _Object.GetNormal(_HitLocation))), 0.0f), specular_power);

            Vector3 IndirectLight = _RenderingSystem.GetIndirectLighting(3.0, 300, _HitLocation);
            Vector3 Caustics = _RenderingSystem.GetCaustics(800.0, 800, _HitLocation);

            Ray rRefract = _Object.Refract(_EyeRay, _HitLocation, _Material._RefractiveIndex);
            Vector3 vReflect = Vector3::normalize(Vector3::reflect(_EyeRay.direction,_Object.GetNormal(_HitLocation)));

            Vector3 cRefract(0.0, 0.0, 0.0);
            Vector3 cReflect(0.0, 0.0, 0.0);

            float shadow = 1.0 - 0.75 * _RenderingSystem.SoftShadow(_HitLocation, 512);

            Vector3 cDiffuse =  shadow*_Material.GetColor() * diffuse + 1.0*IndirectLight + 1.8*Caustics;

            //cout <<  cDiffuse << endl;

            float mixReflect = _Material.GetReflectance();
            float mixTrans = _Material.GetTransparancy();

            if (mixReflect > 0.0)
                cReflect = _RenderingSystem.Trace(Ray(vReflect, _HitLocation, _EyeRay.life));
            else
                specular = 0.0;

            if (mixTrans > 0.0)
                cRefract = _RenderingSystem.Trace(rRefract);

            float mixDiffuse = 1.0 - mixTrans - mixReflect;

            if (mixDiffuse < 0.0)
            {
                mixReflect /= (mixReflect + mixTrans);
                mixTrans /= (mixReflect + mixTrans);
                mixDiffuse = 0.0;
            }

            float R = mixReflect;
            // Fresnel equations
            if (mixReflect > 0.0 && mixTrans > 0.0)
            {
                float Eta = _EyeRay._RefractiveIndex / _Material._RefractiveIndex;
                float F  = ((1.0-Eta) * (1.0-Eta)) / ((1.0+Eta) * (1.0+Eta));
                float power = 2.0;
                R   = F + (1.0 - F) * pow((1.0 - Vector3::dot(_EyeRay.direction,-1.0 * _Object.GetNormal(_HitLocation))), power);
            }


            _ColorOut = Vector3::Mix(Vector3::Mix(cRefract, cReflect, R), cDiffuse, mixDiffuse) + specular * Vector3(1.0, 1.0, 1.0);
        }

        Vector3 Shade(Material &_Material,
                            Ray &_EyeRay,
                            Vector3 &_HitLocation,
                            SurfaceObject &_Object,
                            vector<LightSource*> &_ListOfLightSources,
                            RenderingSystem &_RenderingSystem) {
            if ( Validate() )
            {
                InternalShade(_Material, _EyeRay, _HitLocation, _Object, _ListOfLightSources, _RenderingSystem);
                return _ColorOut;
            }
            return Vector3(0.0, 0.0, 1.0);
        }

        ~PhongShader() {
            //cout << "Destroying PhongShader" << endl;
        }

    private:
};

#endif


